/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 *
 */

import { cloneDeep } from '@baize/wdk';

import { ModuleModelImpl } from '../model/module/module-model-impl.js';
import { ProjectModelImpl } from '../model/project/project-model-impl.js';
import { AppJson } from '../options/configure/app-json-options.js';
import { ModuleJson } from '../options/configure/module-json-options.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';

import { ModuleTargetData } from './data/hap-task-target-data.js';
import { TargetTaskService } from './service/target-task-service.js';
import AppOptObj = AppJson.AppOptObj;
import { TaskNames } from './common/task-names.js';
import { MergeProfile } from './merge-profile.js';
import Task = TaskNames.Task;
import { FileSet } from '@ohos/hvigor';
import { TaskInputValue } from '@ohos/hvigor';
import path from 'path';

import { projectOhosConfigManager } from '../common/global/project-ohos-config-manager.js';
import { BundleType } from '../const/common-const.js';
import { TargetSourceSetModel } from '../model/source-set/source-set-model.js';
import { AppOpt } from '../options/build/app-ohos-config.js';
import { ConfigJson } from '../options/configure/config-json-options.js';

import { AbstractMakePackInfo } from './abstract-make-pack-info.js';
import { INPUT_KEY } from './abstract-merge-profile.js';

/**
 * 生成Stage模型hap级别的pack.info
 *
 * @since 2022/5/9
 */
export class MakePackInfo extends AbstractMakePackInfo {
  private log: OhosLogger = OhosLogger.getLogger(MakePackInfo.name);

  declareInputs(): Map<string, TaskInputValue> {
    const appResOpt = (this.projectModel as ProjectModelImpl).getAppRes().getAppResOpt();
    return super
      .declareInputs()
      .set('releaseType', this.sdkInfo.getReleaseType())
      .set(INPUT_KEY.projectConfigAppOpt, JSON.stringify(projectOhosConfigManager.getOverrides()?.signingConfig))
      .set('integratedHsp', !!this.targetService.getBuildOption().arkOptions?.integratedHsp)
      .set('appResOpt', JSON.stringify(appResOpt));
  }

  declareInputFiles(): FileSet {
    const appJson5Path = (this.projectModel as ProjectModelImpl).getAppRes().getJsonPath();
    const targetSourceSetModel: TargetSourceSetModel = this.targetService.getTargetData().getModuleSourceSetModel() as TargetSourceSetModel;
    const targetRes = targetSourceSetModel.getModuleTargetRes();
    const targetJsonPath = targetRes.getJsonPath();
    const formPathList = MakePackInfo.getFormPathList(this.moduleModel as ModuleModelImpl, this.targetName);
    const buildProfilePath = this.projectModel.getProfilePath();
    const fileSet = new FileSet().addEntry(appJson5Path).addEntry(targetJsonPath).addEntry(buildProfilePath);
    formPathList?.forEach((formPath) => {
      if (formPath) {
        const formConfigPath = path.resolve(targetRes.getResourcePath(), formPath);
        fileSet.addEntry(formConfigPath);
      }
    });
    return fileSet;
  }

  constructor(taskService: TargetTaskService) {
    super(taskService, Task.MAKE_PACK_INFO);
  }

  initTaskDepends() {
    this.declareDepends(MergeProfile.name);
  }

  protected doTaskAction(): void {
    const appJson: AppOptObj = (this.projectModel as ProjectModelImpl).getAppRes().getAppResOpt();
    const appOptFromConfig: AppOpt = cloneDeep(projectOhosConfigManager.getOverrides()?.appOpt);

    const curProduct = this.targetData.getProduct();

    const versionObj: object = {
      code: appOptFromConfig?.versionCode ?? curProduct.versionCode ?? appJson.app.versionCode,
      name: appOptFromConfig?.versionName ?? curProduct.versionName ?? appJson.app.versionName,
      minCompatibleVersionCode: appJson.app.minCompatibleVersionCode,
    };

    let bundleName = appOptFromConfig?.bundleName ?? curProduct.bundleName ?? appJson.app.bundleName;
    const integratedHsp = this.targetService.getBuildOption().arkOptions?.integratedHsp;

    // 如果当前模块时hsp且配置了integratedHsp为true则将pack.info的bundleName置空
    if (this.moduleModel.isHspModule() && integratedHsp) {
      bundleName = '';
    }

    const appObj: object = {
      bundleName: bundleName,
      bundleType: appOptFromConfig?.bundleType ?? curProduct.bundleType ?? appJson.app.bundleType ?? BundleType.APP,
      version: versionObj,
    };

    this.doMakePackInfo(this.log, appObj);
  }

  private processExtensionAbilities(
    extensionAbilitiesObj: ModuleJson.ExtensionAbilityObj[] | undefined,
    moduleModel: ModuleModelImpl,
    targetName: string,
  ): object[] | undefined {
    if (extensionAbilitiesObj === undefined) {
      return undefined;
    }
    const processedExtensionAbilities: object[] = [];
    for (const orgExtensionAbilityObj of extensionAbilitiesObj) {
      const extensionAbilityObj: object = {
        name: orgExtensionAbilityObj.name,
        forms: this.processForms(MakePackInfo.processMetaData(orgExtensionAbilityObj.metadata, moduleModel, targetName)),
      };
      processedExtensionAbilities.push(extensionAbilityObj);
    }
    return processedExtensionAbilities;
  }

  /**
   * 根据metadata标签得到存储卡片信息的文件路径，并根据文件内容得到卡片信息
   *
   * @param metadata module.json5中的metada标签下的信息
   * @param moduleModel 模块的数据管理对象
   * @param targetName 模块的target名
   * @return forms 多个卡片信息对象
   */
  private static processMetaData(
    metadata: ModuleJson.MetadataObj[] | undefined,
    moduleModel: ModuleModelImpl,
    targetName: string,
  ): ModuleJson.FormObj[] | undefined {
    if (metadata === undefined) {
      return undefined;
    }
    const forms: ModuleJson.FormObj[] = [];
    for (const metadataItem of metadata) {
      const resourcePath: string | undefined = MakePackInfo.processResourcePath(metadataItem.resource);
      if (metadataItem.name && metadataItem.name.indexOf('form') >= 0 && resourcePath !== undefined) {
        const formsObj: ModuleJson.FormsObj = moduleModel.getFormJsonObjByTargetName(targetName, resourcePath);
        const orgFormsObj: ModuleJson.FormObj[] = formsObj.forms;
        for (let j = 0; j < orgFormsObj.length; j++) {
          forms.push(orgFormsObj[j]);
        }
      }
    }
    return forms;
  }

  /**
   * 获取模块资源路径集合
   *
   * @param moduleModel moduleModel
   * @param targetName 当前targetName
   * @return formPathList metadata下resource路径集合
   */
  private static getFormPathList(moduleModel: ModuleModelImpl, targetName: string): string[] | undefined {
    const moduleOptObj = moduleModel.getJsonObjByTargetName(targetName);
    const extensionAbilities: ModuleJson.ExtensionAbilityObj[] | undefined = moduleOptObj.module.extensionAbilities;
    if (extensionAbilities === undefined) {
      return undefined;
    }
    const formPathList: string[] = [];
    for (const orgExtensionAbilityObj of extensionAbilities) {
      if (orgExtensionAbilityObj.metadata === undefined) {
        return [];
      }
      for (const metadataItem of orgExtensionAbilityObj.metadata) {
        const resourcePath: string | undefined = MakePackInfo.processResourcePath(metadataItem.resource);
        if (resourcePath) {
          formPathList.push(resourcePath);
        }
      }
    }
    return formPathList;
  }

  /**
   * 解析meatadata标签下的卡片资源路径，格式如$profile:form_config
   *
   * @param resourcePath meatadata标签下的原始卡片资源路径
   * @return string 卡片信息资源路径
   */
  private static processResourcePath(resourcePath: string | undefined): string | undefined {
    if (!resourcePath) {
      return undefined;
    }
    const strArr = resourcePath.split(':');
    if (strArr.length !== 2 || strArr[0].charAt(0) !== '$' || strArr[0].length < 2) {
      return undefined;
    }
    return `base/${strArr[0].substring(1)}/${strArr[1]}.json`;
  }

  getModuleObj(deviceTypes: string[], moduleModel: ModuleModelImpl, apiVersion: ConfigJson.ApiVersionObj | undefined, targetName: string): object {
    const moduleOptObj = moduleModel.getJsonObjByTargetName(targetName);
    const appJson: AppOptObj = (this.projectModel as ProjectModelImpl).getAppRes().getAppResOpt();
    const appOptFromConfig: AppOpt = cloneDeep(projectOhosConfigManager.getOverrides()?.appOpt);
    let realInstallationFree;
    const curBundleType = appOptFromConfig?.bundleType ?? this.targetData.getProduct().bundleType ?? appJson.app.bundleType ?? BundleType.APP;
    switch (curBundleType) {
      case BundleType.ATOMIC_SERVICE:
        realInstallationFree = true;
        break;
      case BundleType.APP:
        realInstallationFree = false;
        break;
      default:
        realInstallationFree = moduleOptObj.module.installationFree;
        break;
    }
    const distroObj: object = {
      moduleType: moduleOptObj.module.type,
      installationFree: realInstallationFree,
      deliveryWithInstall: moduleOptObj.module.deliveryWithInstall,
      moduleName: moduleOptObj.module.name,
    };

    const apiVersionObj: object = {
      compatible: this.compatibleApiVersion,
      releaseType: this.sdkInfo.getReleaseType(),
      target: this.targetSdkVersion ?? this.compileApiVersion,
    };

    return {
      mainAbility: moduleOptObj.module.mainElement,
      deviceType: deviceTypes,
      abilities: this.processAbilities(moduleOptObj.module.abilities),
      extensionAbilities: this.processExtensionAbilities(moduleOptObj.module.extensionAbilities, moduleModel, targetName),
      distro: distroObj,
      apiVersion: apiVersionObj,
    };
  }

  protected getPackageObj(targetData: ModuleTargetData, moduleOptObj: ModuleJson.ModuleOptObj, noSuffixHapName: string): object[] {
    return [
      {
        deviceType: targetData.getTargetDeviceType(),
        moduleType: moduleOptObj.module.type,
        deliveryWithInstall: moduleOptObj.module.deliveryWithInstall,
        name: noSuffixHapName,
      },
    ];
  }
}
